package com.yiduo.outpost.core.security.jwt;

import com.alibaba.fastjson2.JSON;
import com.yiduo.outpost.core.security.TokenProvider;
import com.yiduo.outpost.core.model.constant.SecurityConstant;
import com.yiduo.outpost.core.model.exception.LoginTimeOutException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;

import javax.crypto.SecretKey;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Optional;


/**
 * @author yw
 */
@Slf4j
public class JwtTokenGenerator {

    @Autowired
    private JwtProperties jwtProperties;


    /**
     *  生成jwt-token
     * @param uid 用户id
     * @param subject 用户名
     * @param ttl   token有效期
     * @param role 用户角色
     * @param uuid 每次登录唯一标识
     * @return
     */
    public String generateToken(Long uid, String subject, Duration ttl, String role,String uuid) {
        return SecurityConstant.BEARER_PREFIX + Jwts.builder()
                .setSubject(subject)
                .claim(TokenProvider.USER_ID_KEY, uid)
                .claim(TokenProvider.USER_ROLE_KEY, role)
                .claim(TokenProvider.USER_TOKEN_KEY, uuid)
                .signWith(jwtProperties.getKey(), SignatureAlgorithm.HS512)
                .setExpiration(Date.from(Instant.now()
                        .plusSeconds(Optional.ofNullable(ttl).orElse(jwtProperties.getExpireTime()).getSeconds())))
                .compact();
    }


    /**
     * 解析JWT获取参数
     *
     * @param jwtToken 请求头部存放的Authorization
     */
    public Claims extractClaimsFromToken(String jwtToken) {
        try {
            return (Claims) Jwts.parserBuilder()
                    .setSigningKey(jwtProperties.getKey())
                    .build()
                    .parseClaimsJws(jwtToken)
                    .getBody();
        } catch (ExpiredJwtException error) {
            log.warn("jwtToken {} login time out!", jwtToken, error);
            throw new LoginTimeOutException();
        }
    }

    /**
     * 根据已经获取的请求头获取jwt-token并转换
     *
     * @param authorization 请求头中的属性Authorization，可以在controller上以springmvc注解【@RequestHeader String authorization】方式获取
     */
    public String extractToken(String authorization) {
        if (StringUtils.hasText(authorization) && authorization.startsWith(SecurityConstant.BEARER_PREFIX)) {
            return authorization.substring(SecurityConstant.BEARER_PREFIX.length());
        }
        return null;
    }

    /**
     * 从请求头获取jwt-token并转换
     *
     * @param request 请求体
     * @return
     */
    public String extractToken(ServerHttpRequest request) {
        String bearerToken = Optional.ofNullable(request.getHeaders().getFirst(SecurityConstant.AUTHORIZATION_HEADER))
                .orElse(request.getQueryParams().getFirst(SecurityConstant.AUTHORIZATION_HEADER));
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(SecurityConstant.BEARER_PREFIX)) {
            return bearerToken.substring(SecurityConstant.BEARER_PREFIX.length());
        }
        return null;
    }

    /**
     * 校验jwt-token是否合法
     */
    public boolean validateToken(String jwtToken) {
        try {
            Jwts.parserBuilder()
                    .setSigningKey(jwtProperties.getKey())
                    .build()
                    .parseClaimsJws(jwtToken);
            return true;
        } catch (ExpiredJwtException error) {
            throw new LoginTimeOutException();
        } catch (Exception e) {
            log.debug("Invalid JWT token: {}. {}", jwtToken, e);
            return false;
        }
    }


    public static void main(String[] args) {
        String secretkey = "C+4s3PJmqIbj/fuJD9Uwz428yBwl7ChmK3zKcVGRr5Hsn1Ln6UD/znkWZumdbQJFsiSe3Q3kijiLHBO1smoNkA==";
        SecretKey secretKey = Keys.hmacShaKeyFor(secretkey.getBytes());

        String jwtoken = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJTRVJWSUNFIiwidWlkIjoxLCJhaWQiOiJkZXY3Iiwic3lzdGVtX3R5cGUiOiJhZ2VudF9wcml2YXRlIiwiZXhwIjoxNjI1OTE5Nzc3fQ.k3DlwKLMVusFO3DLey7M8WeYejSBx2zVX6kKq406tP1DFnUHCk6WZjYUqfNYH7yBJ1aZvk_TDR9xG0jUV3gkDw";

        jwtoken = SecurityConstant.BEARER_PREFIX + Jwts.builder()
                .setSubject("SERVICE")
                .claim(TokenProvider.USER_ID_KEY, 1L)
                .signWith(secretKey, SignatureAlgorithm.HS512)
                .setExpiration(Date.from(Instant.now().plusSeconds(Duration.ofDays(5L).getSeconds())))
                .compact()
        ;

        System.out.println(JSON.toJSONString(jwtoken));
    }
}


